home *** CD-ROM | disk | FTP | other *** search
- ┌─────────────────────────────────┐
- │ Programming the Microsoft Mouse │
- └─────────────────────────────────┘
-
- Written for the PC-GPE by Mark Feldman
- e-mail address : u914097@student.canberra.edu.au
- myndale@cairo.anu.edu.au
-
- ┌───────────────────────────────────────────┐
- │ THIS FILE MAY NOT BE DISTRIBUTED │
- │ SEPARATE TO THE ENTIRE PC-GPE COLLECTION. │
- └───────────────────────────────────────────┘
-
-
- ┌────────────┬───────────────────────────────────────────────────────────────
- │ Disclaimer │
- └────────────┘
-
- I assume no responsibility whatsoever for any effect that this file, the
- information contained therein or the use thereof has on you, your sanity,
- computer, spouse, children, pets or anything else related to you or your
- existance. No warranty is provided nor implied with this information.
-
- ┌──────────────┬─────────────────────────────────────────────────────────────
- │ Introduction │
- └──────────────┘
-
- A complete list of mouse function calls can be found in the file GMOUSE.TXT,
- the file contains calls for both Microsoft (2 button) and Genius (3 button)
- modes.
-
- Calling these functions from within a Pascal program is a fairly simple
- matter. This procedure would get the mouse position and button states:
-
- const MOUSEINTR = $33;
-
- procedure GetMousePos(var x, y : word; var button1, button2 : boolean);
- var regs : registers;
- begin
- regs.ax := 3;
- Intr(MOUSEINTR, regs);
- x := regs.cx;
- y := regs.dx;
- button1 := (regs.bx and 1) <> 0;
- button2 := (regs.bx and 2) <> 0;
- end;
-
-
- The mouse position is returned in variables x and y, the button states are
- returned in variable button1 and button2 (true = button is pressed).
-
-
- ┌─────────────────────────┬──────────────────────────────────────────────────
- │ Writing Custom Handlers │
- └─────────────────────────┘
-
- Most mouse drivers do not support SVGA modes, so you must write custom
- handlers if you want mouse support for these modes.
-
- Rather than writing an entire mouse driver, you can write a simple handler
- routine to take care of the graphics and tell the mouse driver to call it
- whenever the mouse does anything. This function is descibed in the GMOUSE.DOC
- file, but this demo Pascal program shows the general idea. It sets mode 13h,
- resets the mouse and waits for a key to be pressed. Whenever you do anything
- to the mouse (moving it or pressing a button) the handler will get called
- and it will draw a pixel on the screen. The color of the pixel depends on
- which buttons are being pressed.
-
- Uses Crt, Dos;
-
- {$F+}
- { called with bl = buttons, cx = x * 2, dx = y }
- procedure Handler; far; assembler;
- asm
-
- { This mouse "handler" just draws a pixel at the current mouse pos }
- pusha
- mov ax, $A000
- mov es, ax
- shr cx, 1
- xchg dh, dl
- mov di, dx
- shr dx, 2
- add di, dx
- add di, cx
- mov al, bl
- inc al
- stosb
- popa
- end;
- {$F-}
-
- begin
- asm
-
- { Set graphics mode 13h }
- mov ax, $13
- int $10
-
- { Initialize mouse driver }
- xor ax, ax
- int $33
-
- { Install custom handler }
- mov ax, SEG Handler
- mov es, ax
- mov dx, OFS Handler
- mov ax, 12
- mov cx, $1F
- int $33
-
- { Wait for a key press }
- xor ah, ah
- int $16
-
- { Back to text mode }
- mov ax, 3
- int $10
- end;
- end.
-
-
-
-
- Alternatively you may wish to write your own interrupt handler to process
- mouse events as they happen. When a mouse event occurs, 3 interrupts are
- generated and the bytes are availble via the COM port.
-
- ┌──────────────────────────┐
- │ Interrupt Port │
- ├──────────────────────────┤
- │ COM1 $0C $3F8 │
- │ COM2 $0B $3F8 │
- └──────────────────────────┘
-
- The three bytes sent are formatted as follows:
-
-
- 1st byte 2nd byte 3rd byte
- ┌─┬─┬─┬─┬─┬─┬─┬─┐┌─┬─┬─┬─┬─┬─┬─┬─┐┌─┬─┬─┬─┬─┬─┬─┬─┐
- │-│1│?│?│X│X│Y│Y││-│0│X│X│X│X│X│X││-│0│Y│Y│Y│Y│Y│Y│
- └─┴─┴─┴─┴─┴─┴─┴─┘└─┴─┴─┴─┴─┴─┴─┴─┘└─┴─┴─┴─┴─┴─┴─┴─┘
- │ │ └┬┘ └┬┘ └────┬────┘ └────┬────┘
- │ │ │ │ │ │
- │ │ │ └────────────┼────────┐ │
- │ │ └────────┐ │ │ │
- │ │ ┌┴┐ ┌────┴────┐ ┌┴┐ ┌────┴────┐
- │ │ ┌─┬─┬─┬─┬─┬─┬─┬─┐┌─┬─┬─┬─┬─┬─┬─┬─┐
- │ │ │ │ │ │ │ │ │ │ ││ │ │ │ │ │ │ │ │
- Left Button ──┘ │ └─┴─┴─┴─┴─┴─┴─┴─┘└─┴─┴─┴─┴─┴─┴─┴─┘
- Right Button ────┘ X increment Y increment
-
-
- The X and Y increment values are in 2's compliment signed char format. (BTW
- thanks go to Adam Seychell for posting this info to comp.os.msdos.programmer).
-
-
- A simple Borland Pascal 7.0 mouse handler follows. First we declare a few
- things we'll need:
-
-
- ─────────────────────────────────────────────────────────────────────┐
- Uses Crt, Dos;
-
- {$F+}
-
- const COM1INTR = $0C;
- COM1PORT = $3F8;
-
- var bytenum : word;
- combytes : array[0..2] of byte;
- x, y : longint;
- button1, button2 : boolean;
- MouseHandler : procedure;
- ─────────────────────────────────────────────────────────────────────┘
-
- The bytenum variable is used to keep track of which byte is expected next
- (ie 0, 1 or 2). The combytes variable is simply an array to keep track of
- bytes received so far. The mouse position will be stored in the x and y
- varaibles (note that this example will not perfrom any range checking).
- Button1 and button2 will be used to store the states of each of the buttons.
- MouseHandler will be used to store the normal mouse driver event handler.
- We'll need it to reset everything once we are finished.
-
- Here's the actual handler:
-
- ─────────────────────────────────────────────────────────────────────┐
- procedure MyMouseHandler; Interrupt;
- var dx, dy : integer;
- var inbyte : byte;
- begin
-
- { Get the port byte }
- inbyte := Port[COM1PORT];
-
- { Make sure we are properly "synched" }
- if (inbyte and 64) = 64 then bytenum := 0;
-
- { Store the byte and adjust bytenum }
- combytes[bytenum] := inbyte;
- inc(bytenum);
-
- { Have we received all 3 bytes? }
- if bytenum = 3 then
- begin
- { Yes, so process them }
- dx := (combytes[0] and 3) shl 6 + combytes[1];
- dy := (combytes[0] and 12) shl 4 + combytes[2];
- if dx >= 128 then dx := dx - 256;
- if dy >= 128 then dy := dy - 256;
- x := x + dx;
- y := y + dy;
- button1 := (combytes[0] And 32) <> 0;
- button2 := (combytes[0] And 16) <> 0;
-
- { And start on first byte again }
- bytenum := 0;
- end;
-
- { Acknowledge the interrupt }
- Port[$20] := $20;
- end;
- ─────────────────────────────────────────────────────────────────────┘
-
- Once again pretty simple stuff. We just read the byte from the com1 port and
- figure out if it's time to do anything yet. If bit 6 is set to 1 then we
- know that it's meant to be the first byte of the 3, so we reset our
- bytenum variable to zero (in a perfect world bytes would always come in 3's
- and we would never need to check, but it never hurts to be careful).
-
- When 3 bytes have been received we simple decode them according to the
- diagram above and update the appropriate variables accordingly.
-
- The 'Port[$20] := $20;' command just lets the interrupt controller know we
- have processed the interrupt so it can send us the next one when it wants to.
-
- Note that the above "handler" does nothing more than keep track of the
- current mouse position and button states. If we were writing a proper mouse
- driver for an SVGA game we would also have to write custom cursor routines.
- I'll leave that bit to you!
-
- To actually install our mouse driver we'll have to set up all the variables,
- save the address of the current mouse handler and install our own. We'll
- also need call the existing mouse driver to set up the COM1 port to make
- sure it sends us the mouse bytes as it receives them. We could do this
- ourselves, but why make life harder than it already is?
-
- ─────────────────────────────────────────────────────────────────────┐
- procedure InitMyDriver;
- begin
-
- { Initialize the normal mouse handler }
- asm
- mov ax, 0
- int $33
- end;
-
- { Initialize some of the variables we'll be using }
- bytenum := 0;
- x := 0;
- y := 0;
- button1 := false;
- button2 := false;
-
- { Save the current mouse handler and set up our own }
- GetIntVec(COM1INTR, @MouseHandler);
- SetIntVec(COM1INTR, Addr(MyMouseHandler));
- end;
- ─────────────────────────────────────────────────────────────────────┘
-
-
- And finally when our program is finished it'll need to clean up after
- itself and return control back to the normal mouse driver:
-
- ─────────────────────────────────────────────────────────────────────┐
- procedure CleanUpMyDriver;
- begin
- SetIntVec(COM1INTR, @MouseHandler);
- end;
- ─────────────────────────────────────────────────────────────────────┘
-
-
- This little bit of source will test the above code. It does nothing more
- than repeatedly write the mouse position and button states to the screen
- until a keyboard key is pressed:
-
- ─────────────────────────────────────────────────────────────────────┐
- begin
- ClrScr;
- InitMyDriver;
- while not keypressed do
- WriteLn(x : 5, y : 5, button1 : 7, button2 : 7);
- CleanUpMyDriver;
- end.
- ─────────────────────────────────────────────────────────────────────┘
-